Skip to content

fix(runtime): collapse duplicate workspace instruction files in one directory - #3578

Merged
Astro-Han merged 1 commit into
apache:mainfrom
shaokeyibb:fix/dedupe-workspace-instruction-files
Aug 23, 2026
Merged

fix(runtime): collapse duplicate workspace instruction files in one directory#3578
Astro-Han merged 1 commit into
apache:mainfrom
shaokeyibb:fix/dedupe-workspace-instruction-files

Conversation

@shaokeyibb

Copy link
Copy Markdown
Contributor

Summary

readWorkspaceInstructions reads every candidate name in a directory and appends each result, so a repository that shares one instruction file across the names different agent CLIs read gets it injected twice. That sharing is the documented way to do it — Claude Code's docs recommend ln -s AGENTS.md CLAUDE.md, and apache/airflow and deepseek-ai/deepseek-harness both ship that link — and the duplicate then consumes most of the 14000-character workspace-instruction budget, squeezing out whatever genuinely different file sits beside it.

Deduplicate on the digest of the cleaned text, scoped to one directory. Digesting rather than comparing realpath catches a byte-identical copy as well as a link, which is the other common way people share these files. Directories stay independent, because the same text at global and project scope is a user repeating themselves on purpose and collapsing it would silently drop a layer. The digest is taken before truncation, so two files that diverge only past the per-file cap still count as different.

Fixes #3577

Verification

Four new tests in workspace-instructions.test.ts: a symlinked CLAUDE.md collapses, a byte-identical copy collapses, files in one directory that genuinely differ are both kept, and identical text at global and project scope is kept at both scopes. The first two fail on main with expected: 1, actual: 2; the last two pass either way and are there so the fix cannot over-collapse.

End-to-end against a directory holding a real AGENTS.md, in both duplicate forms:

before   blocks = project/AGENTS.md, project/CLAUDE.md
after    blocks = project/AGENTS.md

Ran: workspace-instructions (9) and ai-sdk-backend (204) green; biome check on both changed files; npm run check:asf-headers.

packages/runtime typecheck reports one error in responses-wire-contract.test.ts about an alibaba-cn provider literal. Verified pre-existing — the same error appears on a clean origin/main build, and this change touches neither file.

Not run: the rest of the runtime suite, Desktop, Playwright.

Notes

@AGENTS.md — the import form Claude Code's docs also suggest — is not addressed here. Maka does not expand imports, so a one-line CLAUDE.md containing @AGENTS.md is injected as that literal text. It is small and harmless rather than wasteful, but it is also an instruction that means nothing; worth a separate decision about whether to expand or ignore it.

For reference, the three strategies other agents use: read a single filename (Claude Code, Codex CLI, Gemini CLI), first-match-wins across a candidate list (opencode, Pi), or content deduplication (deepseek-harness). First-match-wins would be simpler here, but it discards a CLAUDE.md that genuinely differs from AGENTS.md, which is a supported thing to have.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — investigation, comparison of other agents' loaders, the tests, and the fix.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

…irectory

Sharing one instruction file across the names different agent CLIs read is the
documented way to do it — Claude Code's docs recommend symlinking CLAUDE.md to
AGENTS.md, and apache/airflow and deepseek-ai/deepseek-harness both ship that
link. Maka read every candidate name and appended each one, so such a repository
had the same bytes injected twice and spent most of the 14000-character
workspace-instruction budget on the duplicate.

Deduplicate on the digest of the cleaned text, scoped to one directory. That
catches the symlink and a byte-identical copy alike, where a realpath check
would only catch the link. Directories stay independent: the same text at global
and project scope is a user repeating themselves deliberately. The digest is
taken before truncation, so two files that diverge only past the per-file cap
still count as different.

The shape follows deepseek-harness, which reads several names and ships the
symlink, and reconciles them by content rather than by path.

Generated-by: Claude Code
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 23, 2026 08:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes duplicated workspace-instruction blocks when multiple supported instruction filenames in the same directory (e.g., AGENTS.md, CLAUDE.md, GEMINI.md) resolve to identical effective instruction text (commonly via symlink or copy). It ensures redundant duplicates don’t waste the shared 14k character workspace-instruction budget, while preserving deliberate layering across global (~/.maka) vs project scopes.

Changes:

  • Deduplicate instruction files within a single directory by hashing the cleaned instruction text (SHA-256) before truncation.
  • Keep global and project directories independent (no cross-scope collapsing).
  • Add targeted tests covering symlinked duplicates, byte-identical copies, non-identical siblings, and identical content across scopes.

Required Conclusion (per code-review skill)

  1. Is the current solution optimal for the actual problem? Yes. Digest-based deduplication on cleaned text directly addresses both symlink and byte-identical copy cases without breaking the “multiple different instruction files in one directory” behavior.
  2. If applicable, what production code can be deleted? none identified.
  3. If applicable, what low-quality tests can be deleted or replaced? none identified.
  4. Is a deeper refactor required, and what should the final structure be? No deeper refactor appears required; this is a localized behavioral fix at the correct source of truth (readWorkspaceInstructions).
  5. Is the reviewed revision ready to merge? Yes, based on the change scope and the added regression tests.
  6. What residual risks or verification gaps remain? No protected-area effect (security/licensing/release/governance) is identified in this diff. Verification gap: broader runtime/desktop/e2e suites were not evidenced in the diff, but the change is small and has direct unit coverage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
packages/runtime/src/system-prompt/workspace-instructions.ts Deduplicates same-directory instruction content by SHA-256 digest of cleaned text prior to truncation.
packages/runtime/src/tests/workspace-instructions.test.ts Adds regression tests covering symlink/copy duplicates and ensures no over-collapsing across differing siblings or scopes.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acting on behalf of repository owner WAWQAQ under the standing Kabi authorization for PR review and APPROVE; exact head 1042a9d7546a3540859e1e905901dd106c35947b.

GO: no P0–P2; one non-blocking P3 inline. Full-patch coverage included digest scope/order, pre-truncation identity, prompt budgets, symlink containment, current-main merge compatibility, and all three GitHub discussion endpoints. Focused tests passed 9/9; removing the sole dedupe branch made exactly the two dedupe regressions fail; restored full Runtime suite passed 3,079 with 13 skips and 0 failures. Desktop and Playwright were not run in this pass.

Fresh machine gates: OPEN, non-draft, mergeable; exact-head test is completed/success. This agent-executed approval does not claim to satisfy the repository's separate required-human-review wording. No merge performed.

// Scoped to one directory on purpose: the same text at global and project
// scope is a user repeating themselves deliberately, and collapsing that
// would silently drop a layer.
const seenDigests = new Set<string>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] This set is recreated for each scope read, so the same physical directory is not actually deduplicated when it is both the global and project root. A production-function repro with cwd === <home>/.maka and one <home>/.maka/AGENTS.md produces blocks=2, copies=2, and scopes [global, project]: buildWorkspaceInstructionsPromptFragment calls this function twice, and each call starts with an empty set. This is inside the PR's stated ‘one directory’ contract, not an unrelated enhancement. It is non-blocking because ordinary project roots differ from ~/.maka, but editing the config directory (or a realpath alias of it) still reproduces the duplicate-budget bug. Please share dedupe state by resolved root, or special-case identical resolved roots, and add the same-root regression.

@Astro-Han
Astro-Han merged commit 877dc96 into apache:main Aug 23, 2026
1 check passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
…irectory (apache#3578)

Sharing one instruction file across the names different agent CLIs read is the
documented way to do it — Claude Code's docs recommend symlinking CLAUDE.md to
AGENTS.md, and apache/airflow and deepseek-ai/deepseek-harness both ship that
link. Maka read every candidate name and appended each one, so such a repository
had the same bytes injected twice and spent most of the 14000-character
workspace-instruction budget on the duplicate.

Deduplicate on the digest of the cleaned text, scoped to one directory. That
catches the symlink and a byte-identical copy alike, where a realpath check
would only catch the link. Directories stay independent: the same text at global
and project scope is a user repeating themselves deliberately. The digest is
taken before truncation, so two files that diverge only past the per-file cap
still count as different.

The shape follows deepseek-harness, which reads several names and ships the
symlink, and reconciles them by content rather than by path.

Generated-by: Claude Code

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shaokeyibb shaokeyibb mentioned this pull request Aug 24, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(runtime): a CLAUDE.md symlinked to AGENTS.md is injected into the prompt twice

4 participants